package ex2b.model.estruturas;

public class FilaCircular<T> {
	private int inicio, fim;
	private int size;
	private final String sizeString;
	private Object[] fila;
	private int totalProduzido;
	
	// Para consumir: Primeiro consome depois soma
	// Para produzir: Primeiro produz depois soma
	public FilaCircular( int size ) {
		this.size = size;
		inicio = fim = totalProduzido = 0;
		fila = new Object[size];
		sizeString = Integer.toString( size );
		
		while( --size >= 0 ) {
			fila[size] = null;
		}
	}
	
	// Primeiro produz depois soma
	public void produz( T produto ) throws ProducaoException {
		if( ! podeProduzir() ) {
			throw new ProducaoException("Buffer esta cheio");
		}
		
		totalProduzido++;
		fila[fim] = produto;
		
		fim = ( fim + 1 ) % size;
	}
	
	// Primeiro consome depois soma
	@SuppressWarnings("unchecked")
	public T consome() throws ConsumacaoException {
		T temp = null;
		if( ! podeConsumir() ) {
			throw new ConsumacaoException("Nao ha produtos disponiveis");
		}
		temp = (T) fila[inicio];
		fila[inicio] = null;
		inicio = ( inicio + 1 ) % size;
		return temp;
	}
	
	public boolean podeConsumir() {
		return fila[ inicio ] != null;
	}
	
	public boolean podeProduzir() {
		return fila[ fim ] == null;
	}
	
	// Criados esses dois metodos para permitir o acesso simultaneo de consumidores e possilvemente produtores
	
	// Ele retorna o index que o consumidor deve consumir.
	public int consomeIndex() {
		int temp;
		if( ! podeConsumir() ) {
			throw new ConsumacaoException("Nao ha produtos disponiveis");
		}
		temp = inicio;
		inicio = ( inicio + 1 ) % size;
		return temp;
	}
	
	@SuppressWarnings("unchecked")
	public T consomeEm( int pos ) {
		Object temp = fila[ pos ];
		
		fila[ pos ] = null;
		
		return (T) temp;
	}
	
	// Retorna o index em que o produtor deve colocar o produto...
	public int produzIndex() {
		int temp;
		if( ! podeProduzir() ) {
			throw new ProducaoException("Buffer esta cheio");
		}
		
		temp = fim;
		fim = ( fim + 1 ) % size;
		
		return temp;
	}
		
	public void produzEm( int pos, T produto ) {
		fila[ pos ] = produto;
	}
	
	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append( "{ size: " ).append( sizeString )
					.append( ", inicio: " ).append(  inicio )
					.append( ", fim: " ).append( fim ).append( " }\n{ ");
		for( Object obj : fila ) {
			sb.append( obj == null ? "#": obj.toString() ).append(" ");
		}
		sb.append("}");
		return sb.toString();
	}
	
	public Object[] getProducts() {
		return fila;
	}
	
	public int getTotalProduzido() {
		return totalProduzido;
	}
}
